namespace Xmef.Engine
{
    using System;
    using System.Collections.Generic;
    using Xmef.Interfaces;

    [Serializable]
    public class PluginService<T> : PluginService, IPluginService<T>, IPluginServiceProvider<IPluginService<T>>, IPluginService, IPluginServiceProvider<IPluginService>, IPluginIdentity where T: class, IPluginServiceProvider<T>
    {
        private IDictionary<PluginTypeInstance, IList<T>> _items;

        public PluginService() : base(typeof(T))
        {
            this._items = new Dictionary<PluginTypeInstance, IList<T>>();
        }

        private void AddKey(PluginTypeInstance key, T value)
        {
            if (!this.Contains(key))
            {
                this._items.Add(key, new List<T>());
            }
            this._items[key].Add(value);
        }

        private bool Contains(PluginTypeInstance key)
        {
            return this._items.ContainsKey(key);
        }

        private bool Contains(PluginTypeInstance key, T value)
        {
            return (this._items.ContainsKey(key) && this._items[key].Contains(value));
        }

        public override int Count(Type type)
        {
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (this.Contains(key))
            {
                return this._items[key].Count;
            }
            return 0;
        }

        protected int Count(PluginTypeInstance key)
        {
            if (this.Contains(key))
            {
                return this._items[key].Count;
            }
            return 0;
        }

        protected T Get(PluginTypeInstance key, int index)
        {
            if (this.Contains(key) && ((index >= 0) && (index < this.Count(key))))
            {
                return this._items[key][index];
            }
            return default(T);
        }

        public T GetItem(Type type, int index)
        {
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (this.Contains(key) && ((index >= 0) && (index < this.Count(key))))
            {
                return this._items[key][index];
            }
            return default(T);
        }

        public override IGenericCollection<IPluginObject> GetObjects(Type type)
        {
            int count = Count(type);
            GenericCollection<IPluginObject> collection = new GenericCollection<IPluginObject>();
            for (int i = 0; i < count; i++)
            {
                IPluginObject obj = GetItem(type, i) as IPluginObject;
                if (obj != null)
                {
                    collection.Add(obj);
                }
            }
            return collection;
        }

        public T GetService(int index)
        {
            Type type = typeof(T);
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (this.Contains(key) && ((index >= 0) && (index < this.Count(key))))
            {
                return this._items[key][index];
            }
            return default(T);
        }

        public IEnumerable<T> GetServices()
        {
            Type type = typeof(T);
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (this.Contains(key))
            {
                return this._items[key];
            }
            return null;
        }

        public IEnumerable<T> GetServices(Type type)
        {
            int count = Count(type);
            for (int i = 0; i < count; i++)
            {
                T obj = GetItem(type, i) as T;
                if (obj != null)
                {
                    yield return obj;
                }
            }
        }

        public bool Register(Type type, T value)
        {
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (!this.Contains(key, value))
            {
                this.AddKey(key, value);
                return true;
            }
            return false;
        }

        protected bool Register(PluginTypeInstance key, T value)
        {
            if (!this.Contains(key, value))
            {
                this.AddKey(key, value);
                return true;
            }
            return false;
        }

        public bool Register(IPluginServiceManager manager, T value)
        {
            if (value != null)
            {
                Type type = typeof(T);
                PluginTypeInstance key = new PluginTypeInstance(type, this);
                if (!this.Contains(key, value))
                {
                    PluginServiceManager.RegisterService<T>(manager, this);
                    this.AddKey(key, value);
                    return true;
                }
            }
            return false;
        }

        private void RemoveKey(PluginTypeInstance key, T value)
        {
            if (this.Contains(key, value))
            {
                this._items[key].Remove(value);
                if (this._items[key].Count == 0)
                {
                    this._items.Remove(key);
                }
            }
        }

        public bool Unregister(Type type, T value)
        {
            PluginTypeInstance key = new PluginTypeInstance(type, this);
            if (this.Contains(key, value))
            {
                this.RemoveKey(key, value);
                return true;
            }
            return false;
        }

        protected bool Unregister(PluginTypeInstance key, T value)
        {
            if (this.Contains(key, value))
            {
                this.RemoveKey(key, value);
                return true;
            }
            return false;
        }

        public bool Unregister(IPluginServiceManager manager, T value)
        {
            if (value != null)
            {
                Type type = typeof(T);
                PluginTypeInstance key = new PluginTypeInstance(type, this);
                if (this.Contains(key, value))
                {
                    PluginServiceManager.UnregisterService<T>(manager, this);
                    this.RemoveKey(key, value);
                    return true;
                }
            }
            return false;
        }

        public int ServiceCount
        {
            get
            {
                Type type = typeof(T);
                PluginTypeInstance key = new PluginTypeInstance(type, this);
                if (this.Contains(key))
                {
                    return this._items[key].Count;
                }
                return 0;
            }
        }
    }
}

